package com.bitbakery.plugin.lisp.lang.lexer;

import com.bitbakery.plugin.lisp.LispLanguage;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;

/**
 * Identifies all of the token types (at least, the ones we'll care about) in Common Lisp.
 * Used by the lexer to break a Lisp source file down into tokens.
 */
public interface LispTokenTypes {

    static LispLanguage language = new LispLanguage();


    // Special characters
    IElementType LEFT_PAREN = new LispElementType("(");
    IElementType RIGHT_PAREN = new LispElementType(")");

    IElementType BACKQUOTE = new LispElementType("`");
    IElementType QUOTE = new LispElementType("'");
    IElementType COMMA = new LispElementType(",");
    IElementType COMMA_AT = new LispElementType(",@");
    IElementType SHARP_QUOTE = new LispElementType("#'");

    TokenSet SPECIAL_CHARACTERS = TokenSet.create(BACKQUOTE, QUOTE, COMMA, COMMA_AT, SHARP_QUOTE);


    // Keywords and special forms
    IElementType DEFUN = new LispElementType("defun");
    IElementType DEFMACRO = new LispElementType("defmacro");

    IElementType DEFCLASS = new LispElementType("defclass");
    IElementType DEFCONSTANT = new LispElementType("defconstant");
    IElementType DEFGENERIC = new LispElementType("defgeneric");
    IElementType DEFMETHOD = new LispElementType("defmethod");
    IElementType DEFPACKAGE = new LispElementType("defpackage");
    IElementType DEFPARAMETER = new LispElementType("defparameter");
    IElementType DEFVAR = new LispElementType("defvar");

    IElementType QUOTE_KEYWORD = new LispElementType("quote");
    IElementType LAMBDA = new LispElementType("lambda");
    IElementType COND = new LispElementType("cond");
    IElementType SETQ = new LispElementType("setq");
    IElementType IF = new LispElementType("if");
    IElementType LET = new LispElementType("let");
    IElementType WHEN = new LispElementType("when");
    IElementType UNLESS = new LispElementType("unless");
    IElementType PROGN = new LispElementType("progn");

    TokenSet KEYWORDS = TokenSet.create(QUOTE_KEYWORD, LAMBDA, COND, SETQ, IF, LET, WHEN, UNLESS, PROGN);

    IElementType AUX = new LispElementType("&aux");
    IElementType BODY = new LispElementType("&body");
    IElementType KEY = new LispElementType("&key");
    IElementType OPTIONAL = new LispElementType("&optional");
    IElementType REST = new LispElementType("&rest");

    TokenSet PARAMETER_MODIFIERS = TokenSet.create(AUX, BODY, KEY, OPTIONAL, REST);


    // Comments
    IElementType BLOCK_COMMENT = new LispElementType("block comment");
    IElementType LINE_COMMENT = new LispElementType("line comment");
    TokenSet COMMENTS = TokenSet.create(BLOCK_COMMENT, LINE_COMMENT);


    // Literals
    IElementType STRING_LITERAL = new LispElementType("string literal");
    IElementType NUMERIC_LITERAL = new LispElementType("numeric literal");

    IElementType TRUE = new LispElementType("t");
    IElementType NIL = new LispElementType("nil");
    TokenSet BOOLEAN_LITERAL = TokenSet.create(TRUE, NIL);

    TokenSet LITERALS = TokenSet.create(STRING_LITERAL, NUMERIC_LITERAL, TRUE, NIL);


    IElementType SYMBOL = new LispElementType("symbol");


    // Control characters
    IElementType EOL = new LispElementType("end of line");
    IElementType EOF = new LispElementType("end of file");
    IElementType WHITESPACE = TokenType.WHITE_SPACE;
    IElementType BAD_CHARACTER = TokenType.BAD_CHARACTER;

    TokenSet WHITESPACE_SET = TokenSet.create(EOL, EOF, WHITESPACE);
    // TODO - Not tokens, but we should know what library functions are available depending on the CL implementation we're using??
    // TODO - We should understand the syntax of common macros, like do, print format synatx, etc.
    // TODO - Should we distinguish between macros and functions that are destructive?
}